package albert3d.gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.nio.IntBuffer;

import javax.media.opengl.GL3;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLCanvas;
import javax.swing.JFileChooser;

import com.jogamp.opengl.util.FPSAnimator;
import com.jogamp.opengl.util.GLBuffers;

public class Panel3D extends GLCanvas implements GLEventListener {

	private int vertexShader;
	private int fragmentShader;
	private int shaderProgram;
	private int vertexShader2;
	private int fragmentShader2;
	private int shaderProgram2;
	
	private int modelViewMatrix;
	private int projectionMatrix;
	
	private int vertexData;
	private int faceData;
	private int vertexData2;
	private int faceData2;
	
	private int posLocation;
	private int vao;
	private int posLocation2;
	private int vao2;
	
	
	private Matrix matrix = new Matrix();
	private Shader shader = new Shader();
	private ObjLoader obj;// = new ObjLoader();
	private ObjLoader obj2;// = new ObjLoader();
	private Input input = new Input();
	
	private float angle = 0.0f;
	boolean modelLoaded = false;
	boolean load = false;
	private String path = null;
	int polyMode = 2;
	int faceMode = 2;

	static GLProfile profile = GLProfile.getDefault();
	static GLCapabilities capablities = new GLCapabilities(profile);
	final FPSAnimator animator = new FPSAnimator(this, 60);
	
	boolean pressed = false;
	float x = 0.0f;
	private Menu menu;

	public Panel3D(Menu panelMenu) {
		super(capablities);
		animator.start();
		addGLEventListener(this);
		addMouseListener(input);
		addMouseMotionListener(input);
		addMouseWheelListener(input);
		addKeyListener(input);
		menu = panelMenu;

	}

	@Override
	public void init(GLAutoDrawable drawable) {
		try {
			GL3 gl = drawable.getGL().getGL3();
			
			menu.getImportObjButton().addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					openButtonActionPerformed();
				}
			});
			
			
			
			menu.getResetCameraButton().addActionListener(new ActionListener() {	
				@Override
				public void actionPerformed(ActionEvent arg0) {
					input.x = 0.0f;
					input.y = 0.0f;
					input.z = -8.0f;
					input.angleX = 0.0f;
					input.angleY = 0.0f;
					input.scale = 1.0f;
				}
			});
			
			menu.getResetObjectButton().addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					menu.getScaleSlider().setValue(10);
					menu.getXTranslateSlider().setValue(0);
					menu.getYTranslateSlider().setValue(0);
					menu.getZTranslateSlider().setValue(0);
					menu.getXRotateSlider().setValue(0);
					menu.getYRotateSlider().setValue(0);
					menu.getZRotateSlider().setValue(0);
				}
			});
			
			gl.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
			gl.glClear(GL3.GL_COLOR_BUFFER_BIT | GL3.GL_DEPTH_BUFFER_BIT);
			gl.glEnable(GL3.GL_DEPTH_TEST);
			printVersionInfo(gl);
			
			vertexShader = shader.initVertexShader(gl, "./fx/shader.vert");
			fragmentShader = shader.initFragmentShader(gl, "./fx/shader.frag");
			shaderProgram = shader.initShaderProgram(gl, vertexShader, fragmentShader);
			
			vertexShader2 = shader.initVertexShader(gl, "./fx/shader2.vert");
			fragmentShader2 = shader.initFragmentShader(gl, "./fx/shader2.frag");
			shaderProgram2 = shader.initShaderProgram(gl, vertexShader2, fragmentShader2);
			
			posLocation = gl.glGetAttribLocation(shaderProgram, "inPosition");
			posLocation2 = gl.glGetAttribLocation(shaderProgram2, "inPosition");
				
			printMatrixData(matrix);
			gl.setSwapInterval(1);
			
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	@Override
	public void reshape(GLAutoDrawable drawable, int x, int y, int width,
			int height) {
		GL3 gl = drawable.getGL().getGL3();

		final float aspect = (float) width / (float) height;
		gl.glViewport(0, 0, width, height);
		matrix.loadProjectionIdentity();
		//matrix.perspective(45, aspect, 1, 10);
		matrix.frustum(-0.5f*aspect, 0.5f*aspect, -0.5f, 0.5f, 1.0f, 300.0f);
	}

	@Override
	public void display(GLAutoDrawable drawable) {
		GL3 gl = drawable.getGL().getGL3();
		gl.glClear(GL3.GL_COLOR_BUFFER_BIT | GL3.GL_DEPTH_BUFFER_BIT);
		
		if(input.x == 0.0f &&
		   input.y == 0.0f &&
		   input.z == -8.0f &&
		   input.angleX == 0.0f &&
		   input.angleY == 0.0f &&
		   input.scale == 1.0f)
			menu.getResetCameraButton().setEnabled(false);
		else
			menu.getResetCameraButton().setEnabled(true);
		
		if(menu.getScaleSlider().getValue() == 10 &&
					menu.getXTranslateSlider().getValue() == 0 &&
					menu.getYTranslateSlider().getValue() == 0 &&
					menu.getZTranslateSlider().getValue() == 0 &&
					menu.getXRotateSlider().getValue() == 0 &&
					menu.getYRotateSlider().getValue() == 0 &&
					menu.getZRotateSlider().getValue() == 0)
			menu.getResetObjectButton().setEnabled(false);
		else
			menu.getResetObjectButton().setEnabled(true);
		
		if(load == true) {
			try {
			obj = new ObjLoader();
			obj.loadFile(gl, path);
			
			printObjectData(obj.getInfo(path));
			
			gl.glDeleteVertexArrays(1, new int[] { vao }, 0);
			gl.glDeleteBuffers(1, new int[] { vertexData }, 0);
			gl.glDeleteBuffers(1, new int[] { faceData }, 0);
		
			IntBuffer intBuffer = GLBuffers.newDirectIntBuffer(1);
			gl.glGenVertexArrays(1, intBuffer);
			vao = intBuffer.get(0);
			gl.glBindVertexArray(vao);
		
			vertexData = obj.readVertexBuffer();
			gl.glVertexAttribPointer(posLocation, 3, GL3.GL_FLOAT, false, 0, 0);
			gl.glEnableVertexAttribArray(posLocation);
		
			faceData = obj.readIndexBuffer();
		
			gl.glBindVertexArray(0);
			
			obj2 = new ObjLoader();
			obj2.loadFile(gl, path);
			
			gl.glDeleteVertexArrays(1, new int[] { vao2 }, 0);
			gl.glDeleteBuffers(1, new int[] { vertexData2 }, 0);
			gl.glDeleteBuffers(1, new int[] { faceData2 }, 0);
		
			intBuffer = GLBuffers.newDirectIntBuffer(1);
			gl.glGenVertexArrays(1, intBuffer);
			vao2 = intBuffer.get(0);
			gl.glBindVertexArray(vao2);
		
			vertexData2 = obj.readVertexBuffer();
			gl.glVertexAttribPointer(posLocation2, 3, GL3.GL_FLOAT, false, 0, 0);
			gl.glEnableVertexAttribArray(posLocation2);
		
			faceData2 = obj.readIndexBuffer();
		
			gl.glBindVertexArray(0);
			
			load = false;
			modelLoaded = true;
			
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		if(modelLoaded == true) {
			gl.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
			
			if(!menu.getPolyModePointButton().isEnabled())
				polyMode = 0;
			if(!menu.getPolyModeLineButton().isEnabled())
				polyMode = 1;
			if(!menu.getPolyModeFillButton().isEnabled())
				polyMode = 2;
			
			switch (polyMode) {
			case 0:
				gl.glPolygonMode(GL3.GL_FRONT_AND_BACK, GL3.GL_POINT);
				break;
			case 1:
				gl.glPolygonMode(GL3.GL_FRONT_AND_BACK, GL3.GL_LINE);
				break;
			case 2:
				gl.glPolygonMode(GL3.GL_FRONT_AND_BACK, GL3.GL_FILL);
				break;
			}
		
			gl.glUseProgram(shaderProgram);
			gl.glBindVertexArray(vao);
			gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, vertexData);
		
			obj.rotate(menu.getXRotateSlider().getValue(), menu.getYRotateSlider().getValue(), menu.getZRotateSlider().getValue());
			
			projectionMatrix = gl.glGetUniformLocation(shaderProgram, "projection");
			modelViewMatrix = gl.glGetUniformLocation(shaderProgram, "modelView");
		
			matrix.loadViewIdentity();
			matrix.translatef((menu.getXTranslateSlider().getValue()/10.0f)+input.x,
							  (menu.getYTranslateSlider().getValue()/10.0f)+input.y,
							  (menu.getZTranslateSlider().getValue()/10.0f)+input.z-10.0f);
			matrix.scalef((menu.getScaleSlider().getValue()/10.0f)+input.scale,
						  (menu.getScaleSlider().getValue()/10.0f)+input.scale,
						  (menu.getScaleSlider().getValue()/10.0f)+input.scale);
		
			gl.glUniformMatrix4fv(projectionMatrix, 1, false, matrix.projection, 0);
			gl.glUniformMatrix4fv(modelViewMatrix, 1, false, matrix.view, 0);

			obj.render();
		
			gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, 0);
			gl.glBindVertexArray(0);
			gl.glUseProgram(0);
			
			if(menu.getFaceModeFrontAndBackButton().isEnabled()) {
				if(!menu.getFaceModeBackButton().isEnabled())
					gl.glPolygonMode(GL3.GL_FRONT_AND_BACK, GL3.GL_POINT);
				if(!menu.getFaceModeFrontButton().isEnabled())
					gl.glPolygonMode(GL3.GL_FRONT_AND_BACK, GL3.GL_LINE);
				gl.glLineWidth(2.0f);
				gl.glPointSize(5.0f);
				gl.glUseProgram(shaderProgram2);
				gl.glBindVertexArray(vao2);
				gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, vertexData2);
			
				obj.rotate(menu.getXRotateSlider().getValue(), menu.getYRotateSlider().getValue(), menu.getZRotateSlider().getValue());
				
				projectionMatrix = gl.glGetUniformLocation(shaderProgram2, "projection");
				modelViewMatrix = gl.glGetUniformLocation(shaderProgram2, "modelView");
			
				matrix.loadViewIdentity();
				matrix.translatef((menu.getXTranslateSlider().getValue()/10.0f)+input.x+0.001f,
						  (menu.getYTranslateSlider().getValue()/10.0f)+input.y+0.001f,
						  (menu.getZTranslateSlider().getValue()/10.0f)+input.z-10.0f+0.001f);
				matrix.scalef((menu.getScaleSlider().getValue()/10.0f)+input.scale+0.001f,
					  (menu.getScaleSlider().getValue()/10.0f)+input.scale+0.001f,
					  (menu.getScaleSlider().getValue()/10.0f)+input.scale+0.001f);
			
				gl.glUniformMatrix4fv(projectionMatrix, 1, false, matrix.projection, 0);
				gl.glUniformMatrix4fv(modelViewMatrix, 1, false, matrix.view, 0);

				obj2.render();
			
				gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, 0);
				gl.glBindVertexArray(0);
				gl.glUseProgram(0);
				gl.glPointSize(1.0f);
				gl.glLineWidth(1.0f);
				gl.glPolygonMode(GL3.GL_FRONT_AND_BACK, GL3.GL_FILL);
			}
		
			angle += 0.5f;
			if (angle == 360.f)
				angle = 0.0f;
			printMatrixData(matrix);
		}
		
	}

	@Override
	public void dispose(GLAutoDrawable drawable) {
		GL3 gl = drawable.getGL().getGL3();
		gl.glDeleteVertexArrays(1, new int[] { vao }, 0);
		gl.glDeleteBuffers(1, new int[] { vertexData }, 0);
		gl.glDeleteBuffers(1, new int[] { faceData }, 0);
		gl.glDeleteShader(vertexShader);
		gl.glDeleteShader(fragmentShader);
		gl.glDeleteProgram(shaderProgram);
	}
	
	/*public void setMenuInstance(Menu m) {
		menu = m;
	}*/
	
	private void openButtonActionPerformed() {
		JFileChooser fc = new JFileChooser("./res/");
		fc.showOpenDialog(this);
		File file = null; 
		if( (file = fc.getSelectedFile()) != null) {
			path = file.getAbsolutePath();
			if(path.contains(".obj"))
				modelLoaded = false;
				load = true;
		}
			
	}
	
	private void printVersionInfo(GL3 gl) {
		String s = gl.glGetString(GL3.GL_VERSION);
		menu.getDataLines()[0].setText("GL v : " + s);
		s = gl.glGetString(GL3.GL_SHADING_LANGUAGE_VERSION);
		menu.getDataLines()[1].setText("GLSL v : " + s);
	}
	
	/*private void printMatrixData(Matrix matrix) {
		System.out.println("+--------------------++--------------------+");
		System.out.println("|  PROJECTION MATRIX ||     VIEW MATRIX    |");
		System.out.println("+--------------------++--------------------+");
		System.out.println("+--------------------++--------------------+");
		System.out.format("|% 2.3f|% 2.3f|% 2.3f||% 2.3f|% 2.3f|% 2.3f|\n" ,matrix.projection[0], matrix.projection[4], matrix.projection[8], matrix.projection[12], matrix.view[0], matrix.view[4], matrix.view[8], matrix.view[12]);
		System.out.println("+--------------------++--------------------+");
		System.out.format("|% 2.3f|% 2.3f|% 2.3f||% 2.3f|% 2.3f|% 2.3f|\n" ,matrix.projection[1], matrix.projection[5], matrix.projection[9], matrix.projection[13], matrix.view[1], matrix.view[5], matrix.view[9], matrix.view[13]);
		System.out.println("+--------------------++--------------------+");
		System.out.format("|% 2.3f|% 2.3f|% 2.3f||% 2.3f|% 2.3f|% 2.3f|\n" ,matrix.projection[2], matrix.projection[6], matrix.projection[10], matrix.projection[14] ,matrix.view[2], matrix.view[6], matrix.view[10], matrix.view[14]);
		System.out.println("+--------------------++--------------------+");
		System.out.format("|% 2.3f|% 2.3f|% 2.3f||% 2.3f|% 2.3f|% 2.3f|\n" ,matrix.projection[3], matrix.projection[7], matrix.projection[11], matrix.projection[15] ,matrix.view[3], matrix.view[7], matrix.view[11], matrix.view[15]);
		System.out.println("+--------------------++--------------------+");
	}*/
	
	private void printMatrixData(Matrix matrix) {
		String s = String.format("% 2.2f% 2.2f% 2.2f         |         % 2.2f% 2.2f% 2.2f\n" ,matrix.projection[0], matrix.projection[4], matrix.projection[8], matrix.projection[12], matrix.view[0], matrix.view[4], matrix.view[8], matrix.view[12]);
		menu.getDataLines()[4].setText(s);
		s = String.format("% 2.2f% 2.2f% 2.2f         |         % 2.2f% 2.2f% 2.2f\n" ,matrix.projection[1], matrix.projection[5], matrix.projection[9], matrix.projection[13], matrix.view[1], matrix.view[5], matrix.view[9], matrix.view[13]);
		menu.getDataLines()[5].setText(s);
		s = String.format("% 2.2f% 2.2f% 2.2f         |         % 2.2f% 2.2f% 2.2f\n" ,matrix.projection[2], matrix.projection[6], matrix.projection[10], matrix.projection[14] ,matrix.view[2], matrix.view[6], matrix.view[10], matrix.view[14]);
		menu.getDataLines()[6].setText(s);
		s = String.format("% 2.2f% 2.2f% 2.2f         |         % 2.2f% 2.2f% 2.2f\n" ,matrix.projection[3], matrix.projection[7], matrix.projection[11], matrix.projection[15] ,matrix.view[3], matrix.view[7], matrix.view[11], matrix.view[15]);
		menu.getDataLines()[7].setText(s);
	}
	
	private void printObjectData(String[] s) {
		menu.getDataLines()[9].setText(s[0]);
		menu.getDataLines()[10].setText(s[1]);
		menu.getDataLines()[11].setText(s[2]);
		menu.getDataLines()[12].setText(s[3]);
	}

}
